package org.jsola.hr.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jsola.admin.entity.GregorianCalendarDO;
import org.jsola.common.DateKit8;
import org.jsola.exception.ParamException;
import org.jsola.hr.constant.ConfigCodeConstants;
import org.jsola.hr.constant.DateType;
import org.jsola.hr.constant.ExtraType;
import org.jsola.hr.dto.attendrule.AttendPeriodNoticeDTO;
import org.jsola.hr.dto.attendrule.AttendRuleStaffAddDTO;
import org.jsola.hr.entity.*;
import org.jsola.hr.provider.IGregorianCalendarProviderService;
import org.jsola.hr.provider.IMessageProviderService;
import org.jsola.hr.provider.ISiteConfigProviderService;
import org.jsola.hr.provider.IUserProviderService;
import org.jsola.hr.query.AttendExtraJsonQuery;
import org.jsola.hr.service.*;
import org.jsola.message.dto.WeiXinDTO;
import org.jsola.message.dto.WxMiNiParam;
import org.jsola.message.dto.WxTemplateParam;
import org.jsola.user.core.TokenUser;
import org.jsola.user.entity.UnionOpenIdDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;


/**
 * 考勤定时任务
 *
 * @author zxy
 */
@Slf4j
@Service("hrAttendSysNoticeTaskServiceImpl")
public class AttendSysNoticeTaskServiceImpl implements IAttendSysNoticeTaskService {

    @Autowired
    private ITaskService taskService;

    @Autowired
    private IMessageProviderService messageProviderService;

    @Autowired
    private IAttendPlanService attendPlanService;

    @Autowired
    private IGregorianCalendarProviderService gregorianCalendarProviderService;

    @Autowired
    private IAttendExtraJsonService attendExtraJsonService;

    @Autowired
    private ICompanyService companyService;

    @Autowired
    private ISiteConfigProviderService siteConfigProviderService;

    @Autowired
    private IEmpInfoService empInfoService;

    @Autowired
    private IUserProviderService userProviderService;

    @Override
    public void runTask(String taskId) {
        TaskDO task = taskService.selectDOById(taskId, "");
        if (Objects.nonNull(taskId)) {
            log.info("开始执行定时任务-----------:{}", task);
            try {
                // 拼装当前用户
                TokenUser tokenUser = new TokenUser();
                tokenUser.setSiteId(task.getSiteId());
                tokenUser.setUserId(task.getCreateUserId());
                // 任务的执行参数
                AttendPeriodNoticeDTO attendPeriodNoticeDTO = JSONObject.parseObject(task.getRunParam(), AttendPeriodNoticeDTO.class);

                //执行的时候，查询是否需要考勤通知（可能执行定时任务的时候是需要通知，可是后来更新了通知状态改为不通知）
                AttendPlanDO attendPlanDO = attendPlanService.selectDOById(task.getSubjectId());
                if (attendPlanDO != null && attendPlanDO.getIsRemind()) {
                    // 是否同步节假日
                    if (attendPlanDO.getIsSynchLegalHoliday() != null
                            && attendPlanDO.getIsSynchLegalHoliday()) {
                        // 同步节假日查询该天到底需不需要发送通知
                        org.jsola.admin.query.GregorianCalendarQuery gregorianCalendarQuery = new org.jsola.admin.query.GregorianCalendarQuery();
                        gregorianCalendarQuery.setDate(DateKit8.parseDate(DateKit8.format(new Date(), "yyyy-MM-dd")));
                        List<GregorianCalendarDO> gregorianCalendarDOList = gregorianCalendarProviderService
                                .selectClendar(gregorianCalendarQuery, attendPlanDO.getSiteId());
                        if (CollectionUtils.isEmpty(gregorianCalendarDOList)) {
                            log.error("同步节假日但是日历记录没有生成!");
                        } else {
                            if (gregorianCalendarDOList.get(0).getIsCalendar() != null
                                    && gregorianCalendarDOList.get(0).getIsCalendar()) {
                                if (!DateType.WORK_DAY.getValue().equals(gregorianCalendarDOList.get(0).getDateType())) {
                                    // 休息日  不发送通知
                                    log.info("法定休息日 不需要发送考勤提醒：attendPlanDO: " + attendPlanDO);
                                } else {
                                    // 工作日 发送通知
                                    runTaskItem(task.getSubjectId(), task.getRunType(), attendPeriodNoticeDTO);
                                }
                            } else {
                                // 今天不是公历接口拉的数据 发送通知
                                runTaskItem(task.getSubjectId(), task.getRunType(), attendPeriodNoticeDTO);
                            }
                        }
                    }
                    else {
                        // 不同步直接发送通知
                        runTaskItem(task.getSubjectId(), task.getRunType(), attendPeriodNoticeDTO);
                    }
                } else {
                    log.info("不需要发送考勤提醒：attendPlanDO: " + attendPlanDO);
                }
                // 任务置为已完成
                taskService.taskRunEnd(task.getId(), tokenUser);
            } catch (Exception e) {
                log.info("定时任务task:{}执行异常{}", task.getId(), e);
            }
            log.info("定时任务task:{}执行结束", task.getId());
        }
    }

    /**
     * 发送考勤上下班通知
     * @param attendPlanId 考勤规则id
     * @param runType 发送类型:上班 or 下班
     */
    public void runTaskItem(String attendPlanId, String runType, AttendPeriodNoticeDTO attendPeriodNoticeDTO) {
        AttendPlanDO attendPlanDO = attendPlanService.selectDOById(attendPlanId);
        ParamException.notNull(attendPlanDO, "考勤方案ID不存在，id:" + attendPlanId);
        if (attendPlanDO.getIsRemind() == null || !attendPlanDO.getIsRemind()) {
            //不需要发送公众号消息通知，提醒打卡
            return;
        }
        // 查询该租户的配置信息
        CompanyDO company = companyService.selectDOByIdNotSite(attendPlanDO.getCompanyId());
        ParamException.notNull(company, "考勤方案对应公司不存在,id:" + attendPlanDO.getCompanyId());

        String siteId = company.getSiteId();
        // 公众号appid配置
        String appid = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.CODE_APPID,
                siteId);

        // 通知模板id
        String templateId = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.CODE_TODO_REMINDER,
                siteId);

        // 小程序appid配置
        String miniAppid = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.CODE_MINI_APPID,
                siteId);
        // 小程序跳转配置
        String pagePath = siteConfigProviderService.selectValueByCode(ConfigCodeConstants.CODE_PATH_ATTEND,
                siteId);

        //查询考勤规则对应的人员
        List<AttendRuleStaffAddDTO> staffList = new ArrayList<>();

        AttendExtraJsonQuery attendExtraJsonQuery = new AttendExtraJsonQuery();
        attendExtraJsonQuery.setAttendPalnId(attendPlanId);
        List<AttendExtraJsonDO> attendExtraJsonList = attendExtraJsonService.selectDO(attendExtraJsonQuery, null);
        for (AttendExtraJsonDO attendExtraJson : attendExtraJsonList) {
            // 打卡人员
            if (attendExtraJson.getExtraType().equals(ExtraType.ATTEND_PUNCH_STAFF.getValue())) {
                staffList = JSON.parseArray(attendExtraJson.getJsonData(), AttendRuleStaffAddDTO.class);
            }
        }
        for (AttendRuleStaffAddDTO attendRuleStaffAddDTO : staffList) {
            String empInfoId = attendRuleStaffAddDTO.getEmpInfoId();
            EmpInfoDO empInfo = empInfoService.selectById(empInfoId);

            if (empInfo == null || StringUtils.isBlank(appid)) {
                log.error("发现消息通知参数为空： empInfoId:{},appid:{}", empInfoId, appid);
                continue;
            }

            //推送人员
            UnionOpenIdDO unionOpenIdDO = userProviderService.getUnionOpenByUserId(appid, empInfo.getUserId());
            if (Objects.nonNull(unionOpenIdDO) && StringUtils.isNotBlank(unionOpenIdDO.getOpenId())) {
                TokenUser tokenUser = new TokenUser(empInfo.getUserId(), empInfo.getSiteId());
                try {
                    // 跳转小程序
                    WxMiNiParam wxMiNiParam = new WxMiNiParam();
                    wxMiNiParam.setAppid(miniAppid);
                    wxMiNiParam.setPagepath(pagePath);
                    // 推送微信模板消息
                    WeiXinDTO weiXinDTO = new WeiXinDTO();
                    weiXinDTO.setAppId(appid);
                    weiXinDTO.setTemplateId(templateId);
                    weiXinDTO.setMiniProgram(wxMiNiParam);
                    weiXinDTO.setToUser(unionOpenIdDO.getOpenId());
                    if ("shangban".equals(runType)) {
                        // 上班打卡提醒参数
                        List<WxTemplateParam> paramList = new ArrayList<>();
                        WxTemplateParam param1 = new WxTemplateParam();
                        param1.setName("first");
                        param1.setValue("您好，请不要忘记进行上班打卡「" + company.getName() + "」");
                        paramList.add(param1);
                        WxTemplateParam param2 = new WxTemplateParam();
                        param2.setName("keyword1");
                        param2.setValue("打卡提醒");
                        paramList.add(param2);
                        WxTemplateParam param3 = new WxTemplateParam();
                        param3.setName("keyword2");
                        param3.setValue("上班打卡");
                        paramList.add(param3);
                        WxTemplateParam param4 = new WxTemplateParam();
                        param4.setName("keyword3");
                        param4.setValue(empInfo.getName());
                        paramList.add(param4);
                        WxTemplateParam param5 = new WxTemplateParam();
                        param5.setName("keyword4");
                        param5.setValue(attendPeriodNoticeDTO.getStartPeriod());
                        paramList.add(param5);
                        WxTemplateParam param6 = new WxTemplateParam();
                        param6.setName("remark");
                        param6.setValue("请及时处理。点击查看事项详情");
                        paramList.add(param6);
                        weiXinDTO.setTemplateParamList(paramList);
                        messageProviderService.sendWeChatTempMessage(weiXinDTO, tokenUser);
                    }
                    else {
                        // 下班打卡提醒参数
                        List<WxTemplateParam> paramListXiaban = new ArrayList<>();
                        WxTemplateParam param11 = new WxTemplateParam();
                        param11.setName("first");
                        param11.setValue("您好，请不要忘记进行下班打卡「" + company.getName() + "」");
                        paramListXiaban.add(param11);
                        WxTemplateParam param12 = new WxTemplateParam();
                        param12.setName("keyword1");
                        param12.setValue("打卡提醒");
                        paramListXiaban.add(param12);
                        WxTemplateParam param13 = new WxTemplateParam();
                        param13.setName("keyword2");
                        param13.setValue("下班打卡");
                        paramListXiaban.add(param13);
                        WxTemplateParam param14 = new WxTemplateParam();
                        param14.setName("keyword3");
                        param14.setValue(empInfo.getName());
                        paramListXiaban.add(param14);
                        WxTemplateParam param15 = new WxTemplateParam();
                        param15.setName("keyword4");
                        param15.setValue(attendPeriodNoticeDTO.getEndPeriod());
                        paramListXiaban.add(param15);
                        WxTemplateParam param16 = new WxTemplateParam();
                        param16.setName("remark");
                        param16.setValue("请及时处理。点击查看事项详情");
                        paramListXiaban.add(param16);
                        weiXinDTO.setTemplateParamList(paramListXiaban);
                        messageProviderService.sendWeChatTempMessage(weiXinDTO, tokenUser);
                    }
                } catch (Exception e) {
                    log.error("生成考勤通知定时任务异常,empInfo:{}", empInfo);
                    log.error("生成考勤通知定时任务异常:", e);
                }
            }
        }
    }


}
